#pragma once

#include "SceneParser.h"
#include <xercesc/dom/DOM.hpp>
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/sax/HandlerBase.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <iostream>
#include "Structs.cpp";

using namespace std;
using namespace xercesc;

SceneParser::SceneParser(void)
{
}

SceneParser::~SceneParser(void)
{
}

SQuaternion GetQuaternion(DOMNode* node )
{
	SQuaternion  vec;
	DOMNodeList * nlist = node->getChildNodes();
	for(int i = 0 ; i < nlist->getLength(); i++)
	{			
		DOMNode* cnode = nlist->item(i);						
		const XMLCh* nodeName = cnode->getNodeName();
		if(XMLString::compareIString(nodeName,L"x") == 0)
		{
			const char * val = XMLString::transcode(cnode->getTextContent());
			vec.x = atof(val);			
		}
		else if(XMLString::compareIString(nodeName,L"y") == 0)
		{
			const char * val = XMLString::transcode(cnode->getTextContent());
			vec.y = atof(val);			
		}
		else if(XMLString::compareIString(nodeName,L"z") == 0)
		{
			const char * val = XMLString::transcode(cnode->getTextContent());
			vec.z = atof(val);			
		}
		else if(XMLString::compareIString(nodeName,L"w") == 0)
		{
			const char * val = XMLString::transcode(cnode->getTextContent());
			vec.w = atof(val);
			
		}		
	}
	return vec;
}


SVector3 GetVector3(DOMNode* node )
{
	SVector3 vec;
	DOMNodeList * nlist = node->getChildNodes();
	for(int i = 0 ; i < nlist->getLength(); i++)
	{			
		DOMNode* cnode = nlist->item(i);						
		const XMLCh* nodeName = cnode->getNodeName();
		if(XMLString::compareIString(nodeName,L"x") == 0)
		{
			const char * val = XMLString::transcode(cnode->getTextContent());
			vec.x = atof(val);			
		}
		else if(XMLString::compareIString(nodeName,L"y") == 0)
		{
			const char * val = XMLString::transcode(cnode->getTextContent());
			vec.y = atof(val);			
		}
		else if(XMLString::compareIString(nodeName,L"z") == 0)
		{
			const char * val = XMLString::transcode(cnode->getTextContent());
			vec.z = atof(val);			
		}
	}
	return vec;
}

SVector2 GetVector2(DOMNode* node )
{
	SVector2 vec;
	DOMNodeList * nlist = node->getChildNodes();
	for(int i = 0 ; i < nlist->getLength(); i++)
	{			
		DOMNode* cnode = nlist->item(i);						
		const XMLCh* nodeName = cnode->getNodeName();
		if(XMLString::compareIString(nodeName,L"x") == 0)
		{
			const char * val = XMLString::transcode(cnode->getTextContent());
			vec.x = atof(val);			
		}
		else if(XMLString::compareIString(nodeName,L"y") == 0)
		{
			const char * val = XMLString::transcode(cnode->getTextContent());
			vec.y = atof(val);			
		}
	}
	return vec;
}

STransformation GetTransformation(DOMNode* node )
{
	STransformation vec;
	DOMNodeList * nlist = node->getChildNodes();
	for(int i = 0 ; i < nlist->getLength(); i++)
	{			
		DOMNode* cnode = nlist->item(i);						
		const XMLCh* nodeName = cnode->getNodeName();
		if(XMLString::compareIString(nodeName,L"translation") == 0)
		{			
			vec.Translation = GetVector3(cnode);
		}
		else if(XMLString::compareIString(nodeName,L"Scale") == 0)
		{			
			vec.Scale = GetVector3(cnode);
		}
		else if(XMLString::compareIString(nodeName,L"rotationQuaternion") == 0)
		{
			vec.Rotation = GetQuaternion(cnode);
		}
	}
	return vec;
}

vector<int> GetIndices(DOMNode* node )
{
	vector<int> vec;
	DOMNodeList * nlist = node->getChildNodes();
	for(int i = 0 ; i < nlist->getLength(); i++)
	{			
		DOMNode* cnode = nlist->item(i);						
		const XMLCh* nodeName = cnode->getNodeName();
		if(XMLString::compareIString(nodeName,L"int") == 0)
		{
			char * fv = XMLString::transcode(cnode->getTextContent());
			vec.push_back(atoi(fv));				
		}		
	}
	return vec;
}


vector<SVertice> GetVertices(DOMNode* node )
{
	
	vector<SVertice> vec;
	DOMElement* el = (DOMElement* ) node;	
	DOMNodeList * nlist = el->getElementsByTagName(L"Vertice");
	for(int i = 0 ; i < nlist->getLength(); i++)
	{			
		SVertice vertice;
		DOMNode* cnode = nlist->item(i);						

		DOMNodeList * verticesPieces =  cnode->getChildNodes();
		for(int j = 0 ; j < verticesPieces ->getLength(); j++)
		{	
			DOMNode* pnode = verticesPieces->item(j);
			const XMLCh* nodeName = pnode ->getNodeName();
			if(XMLString::compareIString(nodeName,L"Position") == 0)
			{
				vertice.Position = GetVector3(pnode);				
			}		
			else if(XMLString::compareIString(nodeName,L"Normal") == 0)
			{
				vertice.Normal = GetVector3(pnode);				
			}		
			else if(XMLString::compareIString(nodeName,L"TextureCoord") == 0)
			{
				vertice.TextureCoord = GetVector2(pnode);				
			}		
			else if(XMLString::compareIString(nodeName,L"Tangent") == 0)
			{
				vertice.Tangent = GetVector3(pnode);				
			}		
			else if(XMLString::compareIString(nodeName,L"BiNormal") == 0)
			{
				vertice.Binormal = GetVector3(pnode);				
			}		
		}
		vec.push_back(vertice);
	}
	return vec;
}

vector<SMap> GetMaps(DOMNode* node )
{
	
	vector<SMap> vec;
	DOMElement* el = (DOMElement* ) node;	
	DOMNodeList * nlist = el->getElementsByTagName(L"Map");
	for(int i = 0 ; i < nlist->getLength(); i++)
	{	
		SMap map;
		DOMNode* cnode = nlist->item(i);						
		DOMNodeList * verticesPieces =  cnode->getChildNodes();
		for(int j = 0 ; j < verticesPieces ->getLength(); j++)
		{	
			DOMNode* pnode = verticesPieces->item(j);
			const XMLCh* nodeName = pnode ->getNodeName();
			if(XMLString::compareIString(nodeName,L"MapType") == 0)
			{				
				map.MapType = XMLString::transcode(pnode->getTextContent());
			}		
			else if(XMLString::compareIString(nodeName,L"Path") == 0)
			{
				map.Path = XMLString::transcode(pnode->getTextContent());
			}					
		}
		vec.push_back(map);
	}
	return vec;
}



SCena SceneParser::ParseScene(const char* fileName)
{
	  try {
            XMLPlatformUtils::Initialize();
        }
        catch (const XMLException& toCatch) {
            char* message = XMLString::transcode(toCatch.getMessage());
            cout << "Error during initialization! :\n"
                 << message << "\n";
            XMLString::release(&message);
            //return 1;
        }

        XercesDOMParser* parser = new XercesDOMParser();
        parser->setValidationScheme(XercesDOMParser::Val_Always);
        parser->setDoNamespaces(true);    // optional

        ErrorHandler* errHandler = (ErrorHandler*) new HandlerBase();
        parser->setErrorHandler(errHandler);        

        try {
			parser->parse(fileName);
        }
        catch (const XMLException& toCatch) {
            char* message = XMLString::transcode(toCatch.getMessage());
            cout << "Exception message is: \n"
                 << message << "\n";
            XMLString::release(&message);
            //return -1;
        }
        catch (const DOMException& toCatch) {
            char* message = XMLString::transcode(toCatch.msg);
            cout << "Exception message is: \n"
                 << message << "\n";
            XMLString::release(&message);
            //return -1;
        }
        catch (...) {
            cout << "Unexpected Exception \n" ;
            //return -1;
        }

		SCena cena;
		DOMDocument *dom = parser->getDocument();
		
		///SceneName
		DOMNodeList* sceneNameNode = dom->getElementsByTagName(L"Nome");
		DOMNode* name = sceneNameNode->item(0);
		cena.SceneName = XMLString::transcode(name->getTextContent());

		///Cameras
		DOMNodeList* sceneCams = dom->getElementsByTagName(L"Camera");				
		if(sceneCams->getLength()>0)
		{	
			SCamera cam;
 			for(int i = 0 ; i < sceneCams ->getLength(); i++)
			{				
				DOMNode* camNode = sceneCams ->item(i);								
				for(int j = 0 ; j < camNode->getChildNodes()->getLength(); j++)
				{
					DOMNode* prop =camNode->getChildNodes()->item(j);										
					const XMLCh* nodeName = prop->getNodeName();
					if(XMLString::compareIString(nodeName,L"Name") == 0)
					{
						cam.Name =  XMLString::transcode(prop->getTextContent());
					}
					else if(XMLString::compareIString(nodeName,L"NearPlane") == 0)
					{
						char * np = XMLString::transcode(prop->getTextContent());
						cam.NearPlane = atof(np);											
					}
					else if(XMLString::compareIString(nodeName,L"FarPlane") == 0)
					{
						char * fp = XMLString::transcode(prop->getTextContent());
						cam.FarPlane = atof(fp);						
					}
					else if(XMLString::compareIString(nodeName,L"Fov") == 0)
					{
						char * fv = XMLString::transcode(prop->getTextContent());
						cam.Fov  = atof(fv);						
					}
					else if(XMLString::compareIString(nodeName,L"Position") == 0)
					{
						cam.Position = GetVector3(prop);
					}
					else if(XMLString::compareIString(nodeName,L"Target") == 0)
					{
						cam.Target = GetVector3(prop);
					}						
				}				
			}
			
			cena.Cameras.push_back(cam);
		}


		///Models
		DOMNodeList* modelos = dom->getElementsByTagName(L"Modelo");				
		if(modelos->getLength()>0)
		{				
 			for(int i = 0 ; i < modelos->getLength(); i++)
			{				
				SModelo model;
				DOMNode* modelNode = modelos ->item(i);				
				for(int j = 0 ; j < modelNode->getChildNodes()->getLength(); j++)
				{
					DOMNode* prop =modelNode->getChildNodes()->item(j);										
					const XMLCh* nodeName = prop->getNodeName();
					if(XMLString::compareIString(nodeName,L"Name") == 0)
					{
						model.ModelName =  XMLString::transcode(prop->getTextContent());
					}
					else if(XMLString::compareIString(nodeName,L"Transformation") == 0)
					{
						model.Transformation =  GetTransformation(prop);
					}
					else if(XMLString::compareIString(nodeName,L"Indices") == 0)
					{
						model.Indices =  GetIndices(prop);
					}
					else if(XMLString::compareIString(nodeName,L"Vertices") == 0)
					{
						model.Vertices = GetVertices(prop);
					}
					else if(XMLString::compareIString(nodeName,L"Maps") == 0)
					{
						model.Maps = GetMaps(prop);
					}
				}
				cena.Modelos.push_back(model);
			}			
		}		

		delete parser;
        delete errHandler;        		
		XMLPlatformUtils::Terminate();        
		return cena;
}

